package net.cassandraview;

import java.io.Serializable;
import java.util.ArrayList;
import java.util.List;
import java.util.logging.Level;
import java.util.logging.Logger;

import javax.annotation.PostConstruct;
import javax.faces.bean.ManagedBean;
import javax.faces.bean.ManagedProperty;
import javax.faces.bean.SessionScoped;
import javax.faces.model.SelectItem;

import net.cassandraview.config.ConfigurationManager;
import net.cassandraview.config.Host;

import org.apache.cassandra.thrift.Cassandra;
import org.apache.cassandra.thrift.CfDef;
import org.apache.cassandra.thrift.InvalidRequestException;
import org.apache.cassandra.thrift.KsDef;
import org.apache.thrift.TException;
import org.apache.thrift.protocol.TBinaryProtocol;
import org.apache.thrift.protocol.TProtocol;
import org.apache.thrift.transport.TFramedTransport;
import org.apache.thrift.transport.TSocket;
import org.apache.thrift.transport.TTransport;
import org.richfaces.event.ItemChangeEvent;

/**
 * Class, containing information about user's session-wide data.
 * 
 * Currently this is:
 * - Host he is connected to
 * - Keyspace in this host
 * - Connection itself (Cassandra.Client)
 * 
 */
@ManagedBean(name="userSession")
@SessionScoped
public class UserSession implements Serializable {
	
	private static final long serialVersionUID = 1L;

	private final transient Logger log = Logger.getLogger(UserSession.class.getName());
	
	private transient Cassandra.Client client;

	private transient TTransport transport;
	
	private List<Host> recentConnections = new ArrayList<Host>();
	
	private List<SelectItem> availableKeyspaces = new ArrayList<SelectItem>();
	
	private Host currentHost;
	
	private String currentKeyspace = "system";
	
	private String currentColumnFamily;
	
	@ManagedProperty("#{configurationManager}")
	private ConfigurationManager cfgManager;
	
	@PostConstruct
	protected void configure() {
		log.info("Initializing UserSession");
		try {
			currentHost = cfgManager.getConfiguration().getHost().get(0);
			reconnect(currentHost);
		} catch (NullPointerException e) {
			log.log(Level.SEVERE, "Cannot configure user session. ConfigurationManager does not exist.");
		}
			
	}

	public void reconnect(Host h) {
		if (transport != null) {
			transport.close();
		}
		transport = new TFramedTransport(new TSocket(h.getHost(), Integer.parseInt(h.getPort())));
		TProtocol protocol = new TBinaryProtocol(transport);
		client = new Cassandra.Client(protocol);
		try {
			transport.open();
			List<KsDef> keyspaces = client.describe_keyspaces();
			availableKeyspaces = new ArrayList<SelectItem>();
			for (KsDef ks : keyspaces) {
				SelectItem item = new SelectItem(ks.getName(), ks.getName());
				availableKeyspaces.add(item);
			}
			if (!recentConnections.contains(h)) {
				recentConnections.add(h);
			}
			if (recentConnections.size() > 10)
				recentConnections.remove(0);
			currentHost = h;
		} catch (Exception e) {
			log.log(Level.SEVERE, "Cannot open connection to cassandra at host: "
                    + currentHost.getHost() + ":" + currentHost.getPort(), e);
			client = null;
			transport = null;
		}
	}
	
	public List<MenuEntry> getColumnFamilies() {
		if (connectionActive()) {
			List<MenuEntry> res = new ArrayList<MenuEntry>();
			try {
				KsDef ksdef = client.describe_keyspace(currentKeyspace);
				for (CfDef cf : ksdef.getCf_defs()) {
					String name = cf.getName();
					if (cf.getColumn_type().equals("Super")) {
						name = "(S) " + name;
					}
					res.add(new MenuEntry(name, name.length() > 25 ? name.substring(0, 25) + "..." : name));
				}
			} catch(Exception ex) {
				log.log(Level.SEVERE, "Error!", ex);
			}
			return res;
		}
		return null;
	}
	
	public static class MenuEntry {
		private String name;
		private String label;
		public MenuEntry(String name, String label) {
			this.name = name;
			this.label = label;
		}
		public String getName() {return name;}
		public void setName(String name) {this.name = name;}
		public String getLabel() {return label;}
		public void setLabel(String label) {this.label = label;}
	}
	
	public void columnFamilySelected(ItemChangeEvent event) {
		currentColumnFamily = event.getNewItemName();
	}
	
	public Cassandra.Client getClient() {
		return client;
	}

	public ConfigurationManager getCfgManager() {
		return cfgManager;
	}

	public void setCfgManager(ConfigurationManager cfgManager) {
		this.cfgManager = cfgManager;
	}

	public String getCurrentKeyspace() {
		return currentKeyspace;
	}

	public void setCurrentKeyspace(String currentKeyspace) {
		this.currentKeyspace = currentKeyspace;
	}
	
	public String getHost() {
		return currentHost != null ? currentHost.getHost() + ":" + currentHost.getPort() : null;
	}

	public List<Host> getRecentConnections() {
		return recentConnections;
	}

	public void setRecentConnections(List<Host> recentConnections) {
		this.recentConnections = recentConnections;
	}

	public boolean connectionActive() {
		return client != null && transport != null && transport.isOpen();
	}

	public String getCurrentColumnFamily() {
		return currentColumnFamily;
	}

	public void setCurrentColumnFamily(String currentColumnFamily) {
		this.currentColumnFamily = currentColumnFamily;
	}

	public List<SelectItem> getAvailableKeyspaces() {
		return availableKeyspaces;
	}

	public void setAvailableKeyspaces(List<SelectItem> availableKeyspaces) {
		this.availableKeyspaces = availableKeyspaces;
	}

}
